{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# use python 2 and stanhelper-0.8"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "np.set_printoptions(precision=3)\n",
    "np.set_printoptions(suppress=True)\n",
    "np.set_printoptions(linewidth=60)\n",
    "\n",
    "import pandas as pd\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "from matplotlib import style\n",
    "style.use('ggplot')\n",
    "import itertools\n",
    "\n",
    "from IPython.display import display\n",
    "\n",
    "import stanhelper"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# # LOAD AND PROCESS DATA (or read from PRE-PROCESSED files)\n",
    "\n",
    "# path_to_data = '/Users/alpkucukelbir/files/data/movielens/ml-latest-small/'\n",
    "\n",
    "# # ratings\n",
    "# ratings = pd.read_csv(path_to_data+'ratings.csv', sep=',', index_col='userId')\n",
    "# ratings['rating'] = ratings['rating'].apply(np.ceil).astype('int')\n",
    "# del ratings['timestamp']\n",
    "\n",
    "# # movie names\n",
    "# movies = pd.read_csv(path_to_data+'movies.csv', sep=',', index_col='movieId')\n",
    "# movies = movies.reset_index()\n",
    "# movies.index = np.arange(1, len(movies) + 1)\n",
    "# movies['oldMovieId'] = movies['movieId']\n",
    "# del movies['movieId']\n",
    "# movies.index.name = 'movieId'\n",
    "\n",
    "# ratings['oldMovieId'] = ratings['movieId']\n",
    "# ratings['newMovieId'] = ratings['oldMovieId'].apply(lambda x: movies.index[movies['oldMovieId']==x].values[0])\n",
    "# del ratings['movieId']\n",
    "# ratings['movieId'] = ratings['newMovieId']\n",
    "# del ratings['newMovieId']\n",
    "# ratings = ratings[['movieId','rating','oldMovieId']]\n",
    "\n",
    "# ratings.to_pickle('ratings_reindexed.pickle')\n",
    "# movies.to_pickle('movies_reindexed.pickle')\n",
    "\n",
    "ratings = pd.read_pickle('ratings_reindexed.pickle')\n",
    "movies = pd.read_pickle('movies_reindexed.pickle')\n",
    "\n",
    "# ratings = pd.read_pickle('ratings_1M_reindexed.pickle')\n",
    "# movies = pd.read_pickle('movies_1M_reindexed.pickle')\n",
    "\n",
    "# Data has ratings from 1 to 5. Threshold at 1.\n",
    "ratings['rating'] = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# DEFINE ALL OF THE PARAMETERS IN THIS SCRIPT HERE\n",
    "\n",
    "# internal dimension of PF model\n",
    "K = 10\n",
    "\n",
    "# exponential prior\n",
    "lambd = 1/1e3\n",
    "\n",
    "# training/testing split\n",
    "test_ratio = 0.2 # this is the ratio of users that go into the testing dataset\n",
    "\n",
    "# \"smoothness\" parameter to the Gamma initialization for Stan\n",
    "initprm = 1e2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/yixinwang/py2/lib/python2.7/site-packages/ipykernel_launcher.py:13: DeprecationWarning: \n",
      ".ix is deprecated. Please use\n",
      ".loc for label based indexing or\n",
      ".iloc for positional indexing\n",
      "\n",
      "See the documentation here:\n",
      "http://pandas.pydata.org/pandas-docs/stable/indexing.html#ix-indexer-is-deprecated\n",
      "  del sys.path[0]\n"
     ]
    }
   ],
   "source": [
    "# SUBSET THE DATA (optional)\n",
    "\n",
    "# number of users\n",
    "U = ratings.index.max()\n",
    "ratings_subset = ratings[ratings.index <= U] # pick the first U users \n",
    "\n",
    "# number of items\n",
    "I = 1000 #ratings_subset['movieId'].max()\n",
    "ratings_subset = ratings_subset[(ratings_subset['movieId'] <= I)]\n",
    "\n",
    "# after this subsetting, some users have < 20 ratings\n",
    "# toss those users away\n",
    "ratings_subset = ratings_subset.ix[ratings_subset.groupby(ratings_subset.index).sum()['rating'] >= 20]\n",
    "\n",
    "# reindex the users and update U\n",
    "old_index = ratings_subset.index.unique()\n",
    "new_index = np.arange(1, np.size(old_index)+1)\n",
    "ratings_subset.index = ratings_subset.index.map(lambda x: new_index[old_index==x][0])\n",
    "U = ratings_subset.index.max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# OPTIONAL: CORRUPT DATASET\n",
    "\n",
    "# Users with more than 100 ratings\n",
    "users_with_lots_of_ratings = ratings_subset.index.unique()[ \n",
    "    (ratings_subset.groupby(ratings_subset.index).sum()['rating']>150).values ]\n",
    "\n",
    "# Number of users in training dataset to corrupt\n",
    "top_R_users_to_corrupt      = 20\n",
    "\n",
    "# Ratio of ratings to randomly resample from other movies\n",
    "ratio_of_ratings_to_corrupt = 1\n",
    "\n",
    "for u in users_with_lots_of_ratings[0:top_R_users_to_corrupt]:\n",
    "    number_of_movies_to_corrupt = int(np.floor(ratio_of_ratings_to_corrupt*ratings_subset.xs(u).shape[0]))\n",
    "\n",
    "    this_users_movies      = ratings_subset.xs(u)['movieId'].values\n",
    "    not_this_users_movies  = np.setdiff1d(np.arange(I)+1, this_users_movies)\n",
    "    randomly_chosen_movies = np.random.choice(not_this_users_movies, \n",
    "                                              size=number_of_movies_to_corrupt, \n",
    "                                              replace=False)\n",
    "    this_users_movies[0:number_of_movies_to_corrupt] = randomly_chosen_movies\n",
    "    ratings_subset.xs(u)['movieId'] = this_users_movies\n",
    "# print ratings_subset.groupby(ratings_subset.index).sum()['rating']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Int64Index([ 16,  35,  49,  59,  61,  95,  98, 102, 111, 113, 119, 139, 141,\n",
      "            145, 153, 160, 182, 192, 197, 228],\n",
      "           dtype='int64', name=u'userId')\n"
     ]
    }
   ],
   "source": [
    "users_with_lots_of_ratings = ratings_subset.index.unique()[ \n",
    "    (ratings_subset.groupby(ratings_subset.index).sum()['rating']>150).values ]\n",
    "print(users_with_lots_of_ratings[0:top_R_users_to_corrupt])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "result of training/testing splitting\n",
      "training users: 338\n",
      "testing users: 85\n"
     ]
    }
   ],
   "source": [
    "# TRAINING/TESTING SPLIT\n",
    "\n",
    "# number of useres in train and test sets\n",
    "Utr = int(np.floor((1-test_ratio)*U))\n",
    "Ute = U - Utr\n",
    "\n",
    "# get the training dataset\n",
    "ratings_train = ratings_subset[ratings_subset.index <= Utr]\n",
    "\n",
    "# get the testing dataset (requires reindexing)\n",
    "ratings_test  = ratings_subset[ratings_subset.index > Utr]\n",
    "old_index = ratings_test.index.unique()\n",
    "new_index = np.arange(1, np.size(old_index)+1)\n",
    "ratings_test.index = ratings_test.index.map(lambda x: new_index[old_index==x][0])\n",
    "\n",
    "print('result of training/testing splitting')\n",
    "print('training users: '+str(Utr))\n",
    "print('testing users: '+str(Ute))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "size of training dataset\n",
      "number of nonzero measurements: 22950\n",
      "number of parameters in stan: 13380\n"
     ]
    }
   ],
   "source": [
    "# convert datasets into numpy\n",
    "data_ind = ratings_train.sort_index().index.values\n",
    "data_rat = ratings_train.sort_index().values\n",
    "data_train = np.array(np.concatenate((np.atleast_2d(data_ind).T, data_rat), axis=1), dtype='int')\n",
    "\n",
    "data_ind = ratings_test.sort_index().index.values\n",
    "data_rat = ratings_test.sort_index().values\n",
    "data_test = np.array(np.concatenate((np.atleast_2d(data_ind).T, data_rat), axis=1), dtype='int')\n",
    "\n",
    "print('size of training dataset')\n",
    "print('number of nonzero measurements: '+str(data_train.shape[0]))\n",
    "print('number of parameters in stan: '+str((Utr*K)+(I*K)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# write training dataset to file\n",
    "train_dict = {}\n",
    "train_dict['U'] = Utr;\n",
    "train_dict['I'] = I;\n",
    "train_dict['K'] = K;\n",
    "train_dict['number_entries'] = data_train.shape[0]\n",
    "train_dict['user_index'] = data_train[:,0]\n",
    "train_dict['item_index'] = data_train[:,1]\n",
    "train_dict['rating'] = data_train[:,2]\n",
    "train_dict['lambda'] = lambd\n",
    "train_dict['beta_a'] = 200\n",
    "train_dict['beta_b'] = 1\n",
    "stanhelper.stan_rdump(train_dict, 'train_dict.data.R')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# write initialization to file\n",
    "train_dict_init = {}\n",
    "train_dict_init['theta'] = np.random.gamma(initprm,scale=1.0/initprm, size=(Utr,K))\n",
    "train_dict_init['beta'] = np.random.gamma(initprm,scale=1.0/initprm, size=(I,K))\n",
    "train_dict_init['w'] = [0.99] * Utr\n",
    "stanhelper.stan_rdump(train_dict_init, 'train_dict_init.data.R')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./expp_sparse_w_beta optimize iter=10 data file=train_dict.data.R init=train_dict_init.data.R output file=train_map_output.csv\n"
     ]
    }
   ],
   "source": [
    "# # RUN STAN MAP OPTIMIZATION for a few iterations to get SOME INITIALIZATION\n",
    "\n",
    "# # DO THIS FROM THE COMMAND LINE AND WAIT FOR RESULTS\n",
    "\n",
    "execution_string  = './expp_sparse_w_beta optimize iter=10 '\n",
    "execution_string += 'data file=train_dict.data.R init=train_dict_init.data.R output file=train_map_output.csv'\n",
    "print execution_string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "method = optimize\n",
      "  optimize\n",
      "    algorithm = lbfgs (Default)\n",
      "      lbfgs\n",
      "        init_alpha = 0.001 (Default)\n",
      "        tol_obj = 9.9999999999999998e-13 (Default)\n",
      "        tol_rel_obj = 10000 (Default)\n",
      "        tol_grad = 1e-08 (Default)\n",
      "        tol_rel_grad = 10000000 (Default)\n",
      "        tol_param = 1e-08 (Default)\n",
      "        history_size = 5 (Default)\n",
      "    iter = 10\n",
      "    save_iterations = 0 (Default)\n",
      "id = 0 (Default)\n",
      "data\n",
      "  file = train_dict.data.R\n",
      "init = train_dict_init.data.R\n",
      "random\n",
      "  seed = 667005424\n",
      "output\n",
      "  file = train_map_output.csv\n",
      "  diagnostic_file =  (Default)\n",
      "  refresh = 100 (Default)\n",
      "\n",
      "Initial log joint probability = -3.29161e+06\n",
      "    Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes \n",
      "      10      -63754.1       26.2147        545.14       2.288      0.2288       13   \n",
      "Optimization terminated normally: \n",
      "  Maximum number of iterations hit, may not be at an optima\n"
     ]
    }
   ],
   "source": [
    "! ./expp_sparse_w_beta optimize iter=10 data file=train_dict.data.R init=train_dict_init.data.R output file=train_map_output.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_map_results = stanhelper.stan_read_csv('train_map_output.csv')\n",
    "train_map_init = {}\n",
    "train_map_init['theta'] = train_map_results['theta']\n",
    "train_map_init['beta']  = train_map_results['beta']\n",
    "stanhelper.stan_rdump(train_map_init, 'train_map_init.data.R')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./expp_sparse_w_beta variational iter=30000 adapt engaged=0 eta=0.25 tol_rel_obj=1e-2 output_samples=1 data file=train_dict.data.R init=train_dict_init.data.R output file=train_output.csv\n"
     ]
    }
   ],
   "source": [
    "## RUN STAN VARIATIONAL INFERENCE \n",
    "\n",
    "# DO THIS FROM THE COMMAND LINE AND WAIT FOR RESULTS\n",
    "\n",
    "execution_string  = './expp_sparse_w_beta variational iter=30000 adapt engaged=0 eta=0.25 tol_rel_obj=1e-2 output_samples=1 '\n",
    "execution_string += 'data file=train_dict.data.R init=train_dict_init.data.R output file=train_output.csv'\n",
    "print execution_string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "method = variational\n",
      "  variational\n",
      "    algorithm = meanfield (Default)\n",
      "      meanfield\n",
      "    iter = 30000\n",
      "    grad_samples = 1 (Default)\n",
      "    elbo_samples = 100 (Default)\n",
      "    eta = 0.25\n",
      "    adapt\n",
      "      engaged = 0\n",
      "      iter = 50 (Default)\n",
      "    tol_rel_obj = 0.01 (Default)\n",
      "    eval_elbo = 100 (Default)\n",
      "    output_samples = 1\n",
      "id = 0 (Default)\n",
      "data\n",
      "  file = train_dict.data.R\n",
      "init = train_dict_init.data.R\n",
      "random\n",
      "  seed = 667006625\n",
      "output\n",
      "  file = train_output.csv\n",
      "  diagnostic_file =  (Default)\n",
      "  refresh = 100 (Default)\n",
      "\n",
      "------------------------------------------------------------\n",
      "EXPERIMENTAL ALGORITHM:\n",
      "  This procedure has not been thoroughly tested and may be unstable\n",
      "  or buggy. The interface is subject to change.\n",
      "------------------------------------------------------------\n",
      "\n",
      "\n",
      "\n",
      "Gradient evaluation took 0.059421 seconds\n",
      "1000 transitions using 10 leapfrog steps per transition would take 594.21 seconds.\n",
      "Adjust your expectations accordingly!\n",
      "\n",
      "\n",
      "Begin stochastic gradient ascent.\n",
      "  iter             ELBO   delta_ELBO_mean   delta_ELBO_med   notes \n",
      "   100      -256053.430             1.000            1.000\n",
      "   200      -187167.161             0.684            1.000\n",
      "   300      -178383.804             0.472            0.368\n",
      "   400      -174891.100             0.359            0.368\n",
      "   500      -172430.634             0.290            0.049\n",
      "   600      -170359.774             0.244            0.049\n",
      "   700      -168728.566             0.210            0.020\n",
      "   800      -167355.232             0.185            0.020\n",
      "   900      -166102.440             0.165            0.014\n",
      "  1000      -164994.616             0.150            0.014\n",
      "  1100      -163979.071             0.137            0.012\n",
      "  1200      -162971.414             0.126            0.012\n",
      "  1300      -161962.109             0.116            0.010   MEDIAN ELBO CONVERGED\n",
      "\n",
      "Drawing a sample of size 1 from the approximate posterior... \n",
      "COMPLETED.\n"
     ]
    }
   ],
   "source": [
    "! ./expp_sparse_w_beta variational iter=30000 adapt engaged=0 eta=0.25 tol_rel_obj=1e-2 output_samples=1 data file=train_dict.data.R init=train_dict_init.data.R output file=train_output.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_results = stanhelper.stan_read_csv('train_output.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "297.557402\n",
      "(338,)\n",
      "Int64Index([ 16,  35,  49,  59,  61,  95,  98, 102, 111, 113, 119, 139, 141,\n",
      "            145, 153, 160, 182, 192, 197, 228, 241, 254, 284, 285, 289, 295,\n",
      "            298, 302, 347, 352, 356, 363, 365, 383, 385, 395, 423],\n",
      "           dtype='int64', name=u'userId')\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(array([  3.,   1.,   3.,   6.,   8.,  13.,  15.,  15.,\n",
       "         17., 238.]), array([0.259, 0.333, 0.406, 0.48 , 0.554, 0.627, 0.701,\n",
       "        0.774, 0.848, 0.921, 0.995]), <a list of 10 Patch objects>)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10e6f0c50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "w = train_results['mean_pars']['w']\n",
    "print(np.sum(train_results['mean_pars']['w']))\n",
    "print(train_results['mean_pars']['w'].shape)\n",
    "print(users_with_lots_of_ratings)\n",
    "\n",
    "\n",
    "crpt_wt = w[users_with_lots_of_ratings[0:(top_R_users_to_corrupt-1)]-1]\n",
    "uncrpt_wt = w[np.setdiff1d(np.arange(Utr), users_with_lots_of_ratings[0:(top_R_users_to_corrupt-1)]-1)]\n",
    "plt.hist(crpt_wt)\n",
    "plt.hist(uncrpt_wt)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'boxes': [<matplotlib.lines.Line2D at 0x1101e9a50>],\n",
       " 'caps': [<matplotlib.lines.Line2D at 0x1101f2650>,\n",
       "  <matplotlib.lines.Line2D at 0x1101f2a10>],\n",
       " 'fliers': [<matplotlib.lines.Line2D at 0x1101f2e10>],\n",
       " 'means': [],\n",
       " 'medians': [<matplotlib.lines.Line2D at 0x1101f2dd0>],\n",
       " 'whiskers': [<matplotlib.lines.Line2D at 0x1101e9e90>,\n",
       "  <matplotlib.lines.Line2D at 0x1101e9ed0>]}"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAD8CAYAAACMwORRAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4xLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvAOZPmwAAE6JJREFUeJzt3X9sndV9x/H39a8QZ2FuclMIN55EwWHDsKpT5gqCRPEqFVAVCJNOCEEVDBqpUubR4SkbIW1FaAWb04qoGSFihYFI4KyDu0pjYv+0qmTmOrCtf7iMGAEbNuSHgRTaBOzEd3/cm9QOKb6+vvbj+/B+SSh5Hp/kfpGuPhzOc873yRQKBSRJ6VKXdAGSpOoz3CUphQx3SUohw12SUshwl6QUMtwlKYUMd0lKIcNdklLIcJekFGpI8LM9GitJlclMNSDJcOfNN99M8uOlM8pms4yMjCRdhnRG5513XlnjXJaRpBQy3CUphQx3SUqhKdfcQwg/AL4MHIoxXnKGn2eAB4BrgaPALTHG/6x2oZKk8pUzc38UuPpjfn4N0Fb6ZyPw4MzLkiTNxJThHmP8KfDOxwy5DngsxliIMfYBLSGE5dUqUJI0fdVYc88Bb0y4HirdkyQlZE73uYcQNlJcuiHGSDabncuPl8rS0NDgd1M1rxrhPgy0TrheUbr3ETHG3cDu0mXBgyKaC7nc3PyP5PDwGb/2UlWVe4ipGuH+I2BTCOFJ4PPAL2OMb1Xh75U+or29nSNHjiRdxhlN9z8iLS0tDAwMzFI1+qQrZyvkXuALQDaEMAR8E2gEiDHuAp6luA3yFYpbIW+drWKlC+/656RLkGpCplBIrH9Xwd4ymq5cLjfryx9z1VtmLv5dlD6lZZkpG4d5QlWSUshwl6QUMtwlKYUMd0lKIcNdklLIcJekFDLcJSmFDHdJSiHDXZJSyHCXpBQy3CUphQx3SUohw12SUshwl6QUmtPX7EnVMFdvVpptLS0tSZegFDPcVVPmov+5fdaVBi7LSFIKGe6SlEKGuySlkOEuSSlkuEtSChnukpRChrskpZDhLkkpZLhLUgoZ7pKUQoa7JKWQ4S5JKWS4S1IKGe6SlEKGuySlkOEuSSlkuEtSChnukpRChrskpZDhLkkpZLhLUgoZ7pKUQoa7JKVQQzmDQghXAw8A9cDDMcb7Tvv57wH/CLSUxvx1jPHZKtcqSSrTlDP3EEI9sBO4BrgYWB9CuPi0YXcDMcb4OeBG4O+rXagkqXzlzNw7gFdijK8ChBCeBK4DfjFhTAE4u/T73wXerGaR0kzkcrk5+TPDw8PT/jPSbCkn3HPAGxOuh4DPnzbmW8C/hxD+HFgEfLEq1UlVUE7o3n333Tz22GNs2bKFr3/963zve9/j29/+Nl/5yle4995756BKqbrKWnMvw3rg0Rjj9hDCZcDjIYRLYozjEweFEDYCGwFijGSz2Sp9vDQze/bs4Tvf+Q533HEHDQ0NbNmyhUWLFvGNb3yDXbt2JV2eNG3lhPsw0DrhekXp3kS3AVcDxBj/I4RwFpAFDk0cFGPcDewuXRZGRkYqqVmqug8//JAbbriBkZERstksIyMj3HDDDWzevBm/p5pPzjvvvLLGlRPu+4C2EML5FEP9RuCm08b8H/AnwKMhhD8AzgIOl12tlLCmpiY2b97MwMAAg4ODtLW10d7eTlNTU9KlSRWZcrdMjPE4sAl4DnipeCsOhBDuCSGsKQ27E/hqCOHnwF7glhhjYbaKlqrtsssu4+mnn+bAgQMUCgUOHDjA008/zWWXXZZ0aVJFMoVCYhlcePNNN9Vofli1ahUjIyOMjY2dutfY2Eg2m+WFF15IsDJpstKyTGaqcZ5QlYC33nqLhQsX0traSiaTobW1lYULF/LWW28lXZpUEcNdKslkMmzfvp3333+f7du3k8lMOTmS5q1qbYWUat77779PCOHUdV2dcx/VLr+9Usn4+PjHXku1xHCXJjg5W3fWrlrnN1iaYOnSpdTV1bF06dKkS5FmxHCXStrb21myZAkAS5Ysob29PeGKpMr5QFUqGRgYIJPJUCgU2L9/PwmeAZFmzJm7BCxYsADgVKCf/PXkfanWGO4STDqZWs59ab4z3CWK2x4XL15Ma2srdXV1tLa2snjxYrdDqmYZ7lLJmjVr6Ovr49ixY/T19bFmzZqp/5A0TxnuUsnevXt56KGHOHr0KA899BB79+5NuiSpYnaFlCh2hTx06BAnTpw4da++vp5Pf/rTdoXUvGJXSGkaVq5cyYkTJyadUD1x4gQrV65MuDKpMoa7BPT29tLc3EwulyOTyZDL5Whubqa3tzfp0qSKGO4ScPz4cXbt2kVfXx8ffPABfX197Nq1i+PHjyddmlQRT6hKJQ888AC33347o6OjNDU1cemllyZdklQxw12i+ILsF1988dT16OgoL774oi/IVs1yWUaiGObTuS/Nd4a7VNLQ0EBDQ8NHfi/VIsNdKlmwYAF79uzhV7/6FXv27LFpmGqaUxOp5Ne//jXr16/nxIkT1NfXTzrQJNUaZ+7SBCcD3WBXrTPcpZLT35vqe1RVy/z2SiUtLS20traSyWRobW2lpaUl6ZKkihnuEsV97ldeeSXNzc1kMhmam5u58sor3eeumuUDVQnYsGEDjz76KJlMhvHxcQYHB9m/fz+33HJL0qVJFXHmLk2QyWQm/SrVKsNdAp544gnWrl3LhRdeSF1dHRdeeCFr167liSeeSLo0qSKGu0SxzUB/fz/btm3jvffeY9u2bfT399t+QDXLcJcoLsNcddVVrF69msbGRlavXs1VV13l8oxqlq/Zk4BcLkddXR1Lly5lZGSEbDbL22+/zfj4OMPDw0mXJ53ia/akaVi+fDn19fUcPnyYQqHA4cOHqa+vZ/ny5UmXJlXEcJeAY8eOMTY2xrJly6irq2PZsmWMjY1x7NixpEuTKmK4S8CRI0dYvHgxZ511FoVCgbPOOovFixdz5MiRpEuTKmK4SyXZbJahoSEKhQJDQ0Nks9mkS5IqZrhLJa+99hrNzc3U1dXR3NzMa6+9lnRJUsXKaj8QQrgaeACoBx6OMd53hjEB+BZQAH4eY7ypinVKc+LYsWOMj4+71q6aN+XMPYRQD+wErgEuBtaHEC4+bUwb8DfA6hhjO3DHLNQqzbqlS5dO+lWqVeUsy3QAr8QYX40xjgJPAtedNuarwM4Y47sAMcZD1S1Tmn3t7e0sWbKEuro6lixZQnt7e9IlSRUrJ9xzwBsTrodK9yZaCawMIfSGEPpKyzhSTRkYGKCjo4MDBw7Q0dHBwMBA0iVJFatWy98GoA34ArAC+GkI4dIY46R9ZCGEjcBGgBijuxE0b+RyOQ4fPszjjz/O448/DhR7vC9btszvqWpSOeE+DLROuF5RujfREPCzGOMY8FoIYT/FsN83cVCMcTewu3RZGBkZqahoqdruuusuNm/ezPj4OMePH6ehoYGmpibuuusu/J5qPim1H5hSOeG+D2gLIZxPMdRvBE7fCZMH1gOPhBCyFJdpXi27WmkeWLBgAZ/61KcYHh5m+fLlHD16NOmSpIpNueYeYzwObAKeA14q3ooDIYR7QghrSsOeA94OIfwC+DHwVzHGt2eraKnaduzYwYMPPkhfXx/Hjh2jr6+PBx98kB07diRdmlQRu0JKQGtrK6+++iqNjY1ks1lGRkYYGxvjM5/5DG+88cbUf4E0R+wKKU1DW1sb/f39k+719/fT1taWUEXSzBjuEtDV1UV3dze9vb2MjY3R29tLd3c3XV1dSZcmVcRlGakkn8+zY8cOBgcHaWtro6uri+uvvz7psqRJyl2WMdyl05xcc5fmI9fcpWnK5/N0dnaycOFCOjs7yefzSZckVaxaJ1SlmpbP57n//vvp6enh2muv5dlnn6W7uxvApRnVJGfuEsV97j09PaxevZrGxkZWr15NT0+P+9xVswx3CRgcHKSjo2PSvY6ODgYHBxOqSJoZl2Ukivvcv/vd7/Lcc8+d2i3zpS99yX3uqlnO3CXg8ssvZ+fOnaxbt463336bdevWsXPnTi6//PKkS5MqYrhLwPPPP8+mTZt46qmnWLp0KU899RSbNm3i+eefT7o0qSLuc5ewt4xqh/vcpWmwt4zSxgeqEsXeMl/72tdobm5maGiIFStWcPToUe65556kS5Mq4sxdOk0mM+X/8UrznuEuUTzEtGHDBpqbmwFobm5mw4YNHmJSzXJZRgL279/P0aNH2b59+6n2A3feeSdDQ0NJlyZVxHCXgMbGRs4991xuvvlmRkdHaWpq4rOf/SwHDx5MujSpIi7LSMDo6Cj79u1j3bp1HDp0iHXr1rFv3z5GR0eTLk2qiOEuUXyIesUVV9Df38+5555Lf38/V1xxhQ9XVbMMdwkoFAq8/vrrbNu2jffee49t27bx+uuvk+AhP2lGDHcJaGpqoqOjg61bt3L22WezdetWOjo6aGpqSro0qSKGuwRs2LCBZ555hnfeeQeAd955h2eeeYYNGzYkXJlUGcNdAlatWsWiRYt49913GR8f591332XRokWsWrUq6dKkihjuEsVDTLfddhsXXHABdXV1XHDBBdx2220eYlLNcp+7hIeYlD7O3CWKh5huvfXWSe9QvfXWW2lsbEy6NKkihrsEjI2N8cgjj9Db28vY2Bi9vb088sgjjI2NJV2aVBHDXQJWrlzJ2rVrJ22FXLt2LStXrky6NKkihrtEsZ97Pp+fdIgpn8/T1dWVdGlSRXzNnlSSz+fZsWMHg4ODtLW10dXVxfXXX590WdIk5b5mz3CXTnPyHarSfOQ7VKVpyufzdHZ2snDhQjo7O8nn80mXJFXMfe4SxWC///776enpObXPvbu7G8ClGdUkZ+4SxROqPT09k/a59/T0eEJVNctwl4DBwUE6Ojom3evo6GBwcDChiqSZMdwloK2tjf7+/kn3+vv7aWtrS6giaWYMd4niPvfu7u5JJ1S7u7vd566a5VZIqcR97qoFVd3nHkK4GngAqAcejjHe91vG/SnwQ+CPY4wvTPHXGu6al9znrvmsavvcQwj1wE7gGuBiYH0I4eIzjFsM/AXws+kWK0mqrnLW3DuAV2KMr8YYR4EngevOMG4bcD/wQRXrkyRVoJxwzwFvTLgeKt07JYTwR0BrjPFfq1ibJKlCMz6hGkKoA74L3FLG2I3ARoAYI9lsdqYfL1VdQ0OD303VvHLCfRhonXC9onTvpMXAJcBPQggA5wI/CiGsOf2haoxxN7C7dFnwoZXmIx+oaj4rPVCdUjnhvg9oCyGcTzHUbwRuOvnDGOMvgVPTnBDCT4DuMnbLSJJmyZRr7jHG48Am4DngpeKtOBBCuCeEsGa2C5QkTZ+HmKTTuCyj+azcfe62/FXq5XK5qQdVwfDw8NSDpDliuCv1ygndif3cQwjEGOnu7mbz5s22IFBNsnGYxOR+7oD93FXzDHcJ+7krfQx3Cfu5K30Md4nJ/dwB+7mr5rkVUio52c/95Zdf5qKLLrKfu+alqvZznyWGu+alXC7ntkbNW+5zVyq1t7dz5MiRWf+cudgb39LSwsDAwKx/jj6ZDHfVlCNHjsz6rHquTqjO1eEqfTL5QFUqyefzdHZ2snDhQjo7O8nn80mXJFXMmbvE5BOq1157Lc8++yzd3d0APlRVTXLmLjH5hGpjY6MnVFXzDHcJT6gqfQx3CU+oKn0Md4nJJ1THxsY8oaqa5wNVid88NN26dSs33ngjbW1ttvtVTfOEqmrKdU/8T9IlVNW/bPj9pEtQjbH9gFJpLloDzOUhJtscaLrKDXfX3CUphQx3SUohw12SUshwl0rsLaM0cSukhL1llD7O3CXsLaP0Mdwlir1lDhw4MGlZ5sCBA/aWUc1yWUYCzjnnHO69916+//3vn1qW2bRpE+ecc07SpUkVceYuSSlkuEvAwYMH2bJlC1u3buXss89m69atbNmyhYMHDyZdmlQR2w+opqTpvaO+IFuVKLf9gGvuqimz1Ytl4lbIEAIxRrq7u+0MqZrlzF0qyefz7Nixg5dffpmLLrqIrq4ug13zjl0hpQrZrVHzmV0hJekTzHCXpBQy3CUphQx3SUohw12SUqisfe4hhKuBB4B64OEY432n/fwvgduB48Bh4M9ijP9b5VolSWWacuYeQqgHdgLXABcD60MIF5827L+AVTHGPwR+CPxttQuVJJWvnJl7B/BKjPFVgBDCk8B1wC9ODogx/njC+D7g5moWKUmannLW3HPAGxOuh0r3fpvbgH+bSVGSpJmpam+ZEMLNwCrgyt/y843ARoAYI9lstpofL1WN303VunLCfRhonXC9onRvkhDCF4EtwJUxxg/P9BfFGHcDu0uXhZGRkelVK80Rv5uar0rtB6ZUTrjvA9pCCOdTDPUbgZsmDgghfA54CLg6xnhoeqVKkqptyjX3GONxYBPwHPBS8VYcCCHcE0JYUxr2d8DvAP8UQvjvEMKPZq1iSdKU7AopncaukJrP7AopSZ9ghrskpZDhLkkpZLhLUgr5gmylXi73cQeqq/dnfAir+cRwV+pNN3Sz2ayHmFTzXJaRpBQy3CUphQx3SUohw12SUshwl6QUMtwlKYUMd0lKIcNdklIo0Za/SX2wJNW4KVv+JnlCdcripCSEEF6IMa5Kug5pJlyWkaQUMtwlKYUMd+mjdiddgDRTST5QlSTNEmfukpRC9nOXSkIIPwC+DByKMV6SdD3STDhzl37jUeDqpIuQqsFwl0pijD8F3km6DqkaDHdJSiHDXZJSyHCXpBQy3CUphTzEJJWEEPYCXwCywEHgmzHGf0i0KKlChrskpZDLMpKUQoa7JKWQ4S5JKWS4S1IKGe6SlEKGuySlkOEuSSlkuEtSCv0/mbtE1qaRo9YAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10e51eb50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.boxplot(crpt_wt)\n",
    "plt.boxplot(uncrpt_wt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
